Draft 5/24/2002

 

The PowerKernel Architecture

 

The PowerKernel is a cluster-enabled middleware framework for programming Java back-end modules for a variety of front-ends provided by the PowerKernel to service multiple types of clients.  Using the same simple code model on the back-end, you can access the modules as dynamic content, and this dynamic content may utilize XSL rendering and other built-in functionality.  PowerKernel modules can be accessed as Web Services, or pure Java clients utilizing object serialization as the transport.  The modules methods can be overloaded to access different related services differing only by their method parameters supplied by the client.  Support for session binding and parallel computing through clustered PowerKernels is inherent in the code model. Load-on-demand handlers that service user code can be built on the same model the system itself uses for much of its functionality. 

 

In a simple declarative fashion, the model supports applications that may need asynchronous communication.

 

The system also supports a Tuple construct called the Packet class that can function in manner similar to other Tuple Space parallel computing systems such as Sun JavaSpaces/Jini, IBM TSpaces, and the original David Gelernters Linda.

 

Accessing the PowerKernel

 

We examine the use of pure a Java client accessing back-end services first.

 

import com.neocoretechs.powerspaces.*;

import java.io.*;

import java.util.*;

public class ping

{

        /**

        * Connect to kernel running on defualt port and invoke ping

        * @param argv[0] The URI to connect to

        */

        public static void main(String[] argv) throws Exception {

                        // provide java client with URL

                        PowerSpace PS = new PowerSpace(argv[0]);

                        PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSIPCHandler");

                        //

                     for(int i= 0 ; i < 100000 ; i++) {

                              long st = System.currentTimeMillis();

System.out.println("Start time "+String.valueOf(st));

Object o = pkr.invoke("Ping");

System.out.println("Synch time "+String.valueOf(System.currentTimeMillis()-st)+" ms.  Count: " + i);

                              System.out.println(o);

                     }

                     PS.Unplug();

                      //

        }

}

The first step is to obtain the class using the getRemote method.  This causes the class to be loaded and a remote reference to be created in the client.  PowerKernel back-end methods are invoked static, so the reference essentially contains connection information.  Using the invoke method of the remote reference we can invoke methods on the back-end.

 


 

package com.neocoretechs.powerspaces.server.handler;

import com.neocoretechs.powerspaces.*;

import com.neocoretechs.powerspaces.server.*;

import java.util.*;

import java.io.*;

 

public class PSIPCHandler {

 

public static String PowerKernel_Ping( Integer leg, CustomerConnectionPanel ccp) throws PowerSpaceException

       {

              Long tim = new Long(System.currentTimeMillis());

return tim.toString();

       }

}

 

If we wanted to pass arguments to the method we could write it as follows:

 

public static String PowerKernel_Ping( Integer leg, CustomerConnectionPanel ccp, String foo, Integer bar) throws PowerSpaceException

       {

              Long tim = new Long(System.currentTimeMillis());

return tim.toString()+foo+String.valueOf(bar);

}

 

And the client invocation line would look like this:

 

Object o = pkr.invoke("Ping", "yo", new Integer(1));

 

The command line invocation for the above client module might appear as follows:

 

java ping http://127.0.0.1:8080/nct/servlet/com.neocoretechs.power

spaces.server.PowerSpaceServlet

 

The PowerKernel reflects the methods in your class that have "PowerKernel_" prefixing their names and builds tables of the reflected methods, in a manner similar to the 'Beans' model.  This happens when the class is first referenced.  When the method is invoked the Leg and CustomerConnectionPanel arguments are filled in by the system.  The leg represents the connected cluster port the command originated from and the CustomerConnectionPanel contains session information and well as a properties table that the system and developer may use to bind properties to the session.

 

The CustomerConnectionPanel also allows asynchronous communication with clients and the cluster by providing methods to queue messages to various internal queues.  The getSession()  method can be used to obtain the signature of the current session, typically the Id assigned by the web server.

 

You would replace your package and class names for com.neocoretechs.powerspaces.server.handler in the above examples, in your projects.

 

The PowerKernel can also run standalone using the client above.  When started from the java command, it can be connected using persistent sockets.  In both the Servlet and the standalone configuration, Java object serialization is used for transport.  This is transparent to the client except for the connection string:

 

PowerSpace PS = new PowerSpace("hostname", portnum);  // for tcp/ip socket connect

 

You can also make a back-channel socket connect to the PowerKernel running in a web container like the Servlet engine.


 

Exception Handling

 

All remote exceptions are propagated back to the client if not explicitly caught on the server side.  This simplifies remote development and provides a more robust platform.

If we attempted to invoke our ping method with the wrong numner of parameters, the server would respond with an exception manifesting itself as follows:

 

Remote Exception:

Exception in thread "main" java.lang.NoSuchMethodException: Method Ping not foun

d in com.neocoretechs.powerspaces.server.handler.PSIPCHandler Wrong number of pa

rameters


 

Support for Asynchronous Execution

 

If we wish to delay return from a method call until some other processing occurs we can declare a void return type, which will cause the client to wait until a result is queued for return to the client.

 

Using the queuePacket method of CustomerConnectionPanel

 

            static CustomerConnectionPanel waiterCCP;

public static void PowerKernel_PingWait( Integer leg, CustomerConnectionPanel ccp) throws PowerSpaceException, IOException, ClassNotFoundException

       {

waiterCCP = ccp;    

       }

 

public static String PowerKernel_PingNotify( Integer leg, CustomerConnectionPanel ccp) throws PowerSpaceException, IOException, ClassNotFoundException

       {

              Long tim = new Long(System.currentTimeMillis());

waiterCCP.queuePacket(tim.toString());

return "Ok";

       }

In the client application, two threads containing client connections and remote references would, perhaps not in the same Virtual Machines or on the same physical machines, invoke the above methods.  PingWait would wait until PingNotify had been called and queued the payload with the system time back to the other client.

 

Since this system is primarily request/response based, as is most dynamic content especially regarding the expectations of browser clients, the void return type was chosen as the most semantically relevant construct.  The model does require return types where one may not desire to do so, and in that case returning a simple string such as "Ok" was deemed adequate to fulfill the request/response contract.


Dynamic Content Generation

 

Using the testContent method in our PSIPCHandler class, we can type the URI for this method into the browser and return the results as dynamic content.  The arguments encoded in the query string of the URL are transformed into a Hashtable by the ContentServlet.  The ContentServlet passes it to the class and the method encoded in the request.  The Hashtable passed to the testContent method is turned in to a string of key=value items. The method returns a type of TextContentType containing the created string.  A client requesting services through the

 

com.neocoretechs.powerspaces.server.ContentServlet

 

or the

 

com.neocoretechs.powerspaces.server.TransientContentServlet

 

is expected to return a type

derived from ContentTypeInterface.  The TransientContentServlet is a version of ContentServlet that does not create a session binding or send a cookie.  This is used for higher-performance apps that don't require a session to operate.

 

The URI:

 

http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.ContentServlet?class=com.neocoretechs.powerspaces.server.handler.PSIPCHandler&method=testContent&this=youre%20the%20man&that=dawg

 

Produces the resulting:

 

this=youre the man

that=dawg

 

 
On the server side the testContent method appears as follows:

 

public static ContentTypeInterface PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, Hashtable args) throws Exception {

                Enumeration e1 = args.keys();

                StringBuffer sb = new StringBuffer();

                // iterate thru elements in hashtable, extracting key/value

                // pairs...return as text content to browser

                for(int i = 0; i < args.size(); i++) {

                        String elem = (String)e1.nextElement();

                        sb.append(elem);

                        sb.append("=");

                        // elems come back as string array...there may be

                        // multiple elements per name depending on your

                        // request format...

                        String[] elems = (String[])args.get(elem);

                        for(int j = 0 ; j < elems.length; j++)

                                sb.append(elems[j]+" ");

                        sb.append("\r\n");

                }

                return new TextContentType(sb.toString());

        }

 

In addition to the TextContentType, the GifContentType is available for returning dynamic images.  In the above example, we could replace

 

return new TextContentType(sb.toString());

 

With

 

return new GifContentType(toImageByteArray(sb.toString()));

 

Assuming we had a method called toImageByteArray that took a string and converted it to an array of GIF image bytes with the string text.


The HTMLContentType is located in located in the com.neocoretechs.xslt package and is provides not only a straight HTML string as a constructor argument, but also has overloaded constructor methods for supplying the XSL style sheet file or Reader, XML input string, and other optional overloads.

 

 

/**

*Constructor for plain ole HMTL

*/

    public HTMLContentType(String htmlText) {

        content = htmlText;

    }

 

    public HTMLContentType(Reader xmlInput, Reader stylesheet) throws Exception {

         content = processTransformation(new InputSource(xmlInput), new InputSource(stylesheet));

    }

 

    public HTMLContentType(File xmlInput, File stylesheet) throws Exception {

        content = processTransformation(fileInputSource(xmlInput), fileInputSource(stylesheet));

    }

 

    public HTMLContentType(File xmlInput, File stylesheetOne, File stylesheetTwo)

        throws Exception {

        String result = processTransformation(fileInputSource(xmlInput), fileInputSource(stylesheetOne));

        InputSource firstResultStream = new InputSource(new StringReader(result));

        content = processTransformation(firstResultStream, fileInputSource(stylesheetTwo));

    }

 

    public HTMLContentType(String txml, File stylesheet) throws Exception {

        content = processTransformation(new InputSource(new ByteArrayInputStream(txml.getBytes())), fileInputSource(stylesheet));

    }


Getting Data to The Model

 

Getting data into the model in web-based applications has spawned quite a few diverse technologies.  Fundamentally, HTTP has two ways to do this: GET and POST.  The PowerKernel supports both GET and POST transparently, producing a cohesive method call of the same type regardless of HTTP method.  The above URI's in the examples could have been formatted using a HTML FORM tag and POST and produced the same result.  We saw an example of a Hashtable being formed from arbitrarily named parameters in the query string of the URL that produced "youre the man, dawg" in the above example.  If we had formatted it using the reserved names param0, param1, param2… then the request would have been passed to a method with multiple string arguments rather than a Hashtable.  So the URI:

 

http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.ContentServlet?class=com.neocoretechs.powerspaces.server.handler.PSIPCHandler&method=testContent&param0=youre%20the%20man&param1=dawg

 

Would attempt to invoke the method:

 

public static ContentTypeInterface PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, String you, String man) throws Exception { }

 

A series of parameters with the designation param0 will create an array of Objects with the values and pass those to the method:

 

http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.ContentServlet?class=com.neocoretechs.powerspaces.server.handler.PSIPCHandler&method=testContent&param0=youre%20the%20man&param0=dawg

 

Would attempt to invoke the method:

 

public static ContentTypeInterface PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, Object[] mananddawg) throws Exception { }

 

and the call would contain an array of the 2 elements


Forms Submission for Dynamic Content Method Call

 

Many times the GET method is insufficient for complex applications.  Where there are many fields or simply a forms-based app, or perhaps where the argument length exceeds the limit for GET, a forms POST must be employed.  We will attempt to combine several concepts and demonstrate a forms POST generated from an XSL style sheet with JavaScript methods to allow us to dynamically set the form properties for class and method at runtime.  By employing these methods, a generic forms-based submission system is constructed.  The JavaScript to do the actual POST is as follows:

 

<script language="JavaScript1.2">

<!--

var server = '../../servlet/com.neocoretechs.powerspaces.server.ContentServlet';

 

function doSubmit(tclass, tmethod, tform) {

        tform.action = server;

        tform.elements['class'].value = tclass;

        tform.elements['method'].value = tmethod;

        alert("Submitting "+tform.action+" "+tform.elements['class'].value+" "+tform.elements['method'].value);

        tform.submit();

}

 

Where the arguments are class name, method name, and FORM object to POST.

And an example of calling this function from another JavaScript function:

 

function submitRec(form1) {     

parent.gatewayFrame.doSubmit('com.neocoretechs.imagepump.VectorMapHandler','updateCurrent', form1);

}

 

And the function to locate the form via the index in the XML structure in order to submit one of many forms on the page:

 

function submitRec(index) {

window.opener.submitRec(document.forms['form'+index]);

}

 

The XSL style sheet containing the content to transform will be working on this XML structure:

 

<layer name="Underwater">

        <schema>

                <field type="String" size="40" scale="0" display="A corp">Company</field>

                <field type="String" size="20" scale="0" display="Stock thingie">Symbol</field>

                <field type="String" size="45" scale="0" display="Where crashing">Exchange</field>

        </schema>

        <record index="0">

              <field index="1">XML Innovations</field>

              <field index="2">XMLI</field>

              <field index="3">Nasdaq NMS</field>

       </record>

        <record index="1">

              <field index="1">My 2by4</field>

                <field index="2">BYBY</field>

              <field index="3">Nasdaq NMS</field>

       </record>

        <record index="2">

                <field index="1">FlatStone</field>

              <field index="2">FKED</field>

                <field index="3">Nasdaq NMS</field>

       </record>

</layer>

 

And the relevant section in the XSL sheet appears as follows:

 

<TABLE cols="4">

           <xsl:for-each select="layer/record">

                 <form>

                  <xsl:attribute name="name">form<xsl:value-of select="@index"/></xsl:attribute>

                  <xsl:attribute name="action">#</xsl:attribute>

                 

 

                   <input name="class" type="hidden" value="#"></input>

                   <input name="method" type="hidden" value="#"></input>

 

                   <input>

                   <xsl:attribute name="name">param0</xsl:attribute>

                   <xsl:attribute name="type">hidden</xsl:attribute>

                   <xsl:attribute name="value"><xsl:value-of select="//layer/@name"/></xsl:attribute>

                   </input>

                   <input>

                   <xsl:attribute name="name">param1</xsl:attribute>

                   <xsl:attribute name="type">hidden</xsl:attribute>

                   <xsl:attribute name="value"><xsl:value-of select="@index"/></xsl:attribute>

                   </input>

 

                   <xsl:for-each select="field[@index]">

                     <TR bgColor="#ffffff">

                       <TD width="150">

                                        <xsl:variable name="index"><xsl:value-of select="@index"/></xsl:variable>

                                        <xsl:value-of select="/layer/schema/field[number($index)]/@display"/>

                       </TD>

                        <TD width="500">

                                                <FONT size="-1"></FONT>

                                                   <input>

                                                     <xsl:attribute name="type">text</xsl:attribute>

                                                     <xsl:attribute name="name">param2</xsl:attribute>

                                        <xsl:variable name="index"><xsl:value-of select="@index"/></xsl:variable>

                                                     <xsl:attribute name="size"><xsl:value-of select="/layer/schema/field[number($index)]/@size"/></xsl:attribute>

                                                     <xsl:attribute name="value"><xsl:value-of select="current()"/></xsl:attribute>

                                                   </input>

                         </TD>

                      </TR>

                  </xsl:for-each>

                </form>

                <TR bgColor="#ffffff">

                    <TD width="500">

                          <FONT size="-1"></FONT>

                                <form name="submitform" >

                                       <input>

                                        <xsl:attribute name="type">button</xsl:attribute>

                                                <xsl:attribute name="name">Submit1</xsl:attribute>

                                                <xsl:attribute name="value">Submit</xsl:attribute>

                                                <xsl:attribute name="onClick" >submitRec(<xsl:value-of select="@index"/>)</xsl:attribute>

                                       </input>

                                 </form>

                      </TD>

                 </TR>

          </xsl:for-each>

         </TABLE>

 


What we in effect have above is a generic form that contains as many text boxes as there are records in the XML.  Each can be edited and submitted to the PowerKernel methods with a submit button.  If we rendered the sheet against the XML we would produce the following:

 

 

And the rendered portion would appear as follows in the right hand pane:

 

Looking at the XSL, we see that the name of the form is set to the index, the class and method are filled in dynamically by the JavaScript functions, and the first two parameters to the PowerKernel method are the 'layer name' and index, placed in param0 and param1.  For each text box that appears, the name param2 is assigned, which as we discussed earlier creates an array that is passed to the third argument of the PowerKernel method.

 

In conclusion, we explored how data gets into the model, how to manipulate the view, and the ways in which PowerKernel acts as a controller and handles much of the housekeeping associated with a more declarative approach such as configuration files, etc.

 

Rich dynamic content generation systems can be, and have been, developed using the PowerKernel.  Some examples are: Web-based geographic information system, advertising delivery engine, pluggable VPN dynamic configuration controller, and real-time wireless remote GPS tracking system with dynamic mapping.  In addition, the content is completely separate from the business logic.  No code in pages, sans custom tag libraries, and therefore more maintainable.


 

Accessing the PowerKernel  Web Services

 

The back-end modules of the PowerKernel can be accessed as Web services by pointing at the proper URI.  By sending a request to the SOAPRouterServlet with the class name encoded as a parameter in the query string, you can receive the WSDL XSD for use in Web services development environments or clients.

 

In this example we are using the PowerKernel through Tomcat under the old Servlet model, where we use explicit paths vs. WAR files.  The system can be repackaged by the user for the new spec.

 

http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet?WSDL=com.neocoretechs.powerspaces.server.handler.PSIPCHandler.wsdl

 

Here, we are asking the SOAPRouterServlet for the definition of  com.neocoretechs.powerspaces.server.handler.PSIPCHandler.class.  There are many methods in this class with the Ping method highlighted below.

 

The output of the WSDL definition in its entirety follows:

 

<?xml version="1.0" encoding="UTF-8" ?>

- <definitions targetNamespace="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:serviceNS="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsd1="http://www.neocoretechs.com/schema">

- <types>

- <schema elementFormDefault="qualified" targetNamespace="http://www.neocoretechs.com/schema" xmlns="http://www.w3.org/1999/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

- <complexType name="ArrayOfdouble">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="double[]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOfdouble2D">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="double[][]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOffloat">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOffloat2D">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[][]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOfstring">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOfstring2D">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[][]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOfint">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="ArrayOfint2D">

- <complexContent>

- <restriction base="SOAP-ENC:Array">

  <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[][]" />

  </restriction>

  </complexContent>

  </complexType>

- <complexType name="base64Ofbyte">

- <all>

  <element name="element" type="xsd:base64Binary" />

  </all>

  </complexType>

  </schema>

  </types>

- <message name="ConnectRequest">

  <part name="Connect_param0" type="xsd:int" />

  <part name="Connect_param1" type="xsd:int" />

  <part name="Connect_param2" type="xsd:string" />

  <part name="Connect_param3" type="xsd:string" />

  </message>

- <message name="PingResponse">

  <part name="return" type="xsd:string" />

  </message>

- <message name="ConnectNewPowerPlantResponse">

  <part name="return" type="xsd:ur-type" />

  </message>

- <message name="InstallHandlerResponse">

  <part name="return" type="xsd:ur-type" />

  </message>

- <message name="testContentRequest">

  <part name="testContent_param0" type="xsd:Map" />

  </message>

- <message name="PingRequest">

  <part name="Ping_param0" type="xsd:string" />

  </message>

  <message name="CollectRequest" />

- <message name="ConnectResponse">

  <part name="return" type="xsd:ur-type" />

  </message>

- <message name="ConnectNewPowerPlantRequest">

  <part name="ConnectNewPowerPlant_param0" type="xsd:int" />

  <part name="ConnectNewPowerPlant_param1" type="xsd:string" />

  <part name="ConnectNewPowerPlant_param2" type="xsd:string" />

  </message>

- <message name="InstallHandlerRequest">

  <part name="InstallHandler_param0" type="xsd:string" />

  <part name="InstallHandler_param1" type="xsd1:base64Ofbyte" />

  </message>

- <portType name="PSIPCHandlerPortType">

- <operation name="Connect">

  <input message="serviceNS:ConnectRequest" />

  <output message="serviceNS:ConnectResponse" />

  </operation>

- <operation name="Ping">

  <input message="serviceNS:PingRequest" />

  <output message="serviceNS:PingResponse" />

  </operation>

- <operation name="ConnectNewPowerPlant">

  <input message="serviceNS:ConnectNewPowerPlantRequest" />

  <output message="serviceNS:ConnectNewPowerPlantResponse" />

  </operation>

- <operation name="InstallHandler">

  <input message="serviceNS:InstallHandlerRequest" />

  <output message="serviceNS:InstallHandlerResponse" />

  </operation>

- <operation name="InstallHandler">

  <input message="serviceNS:InstallHandlerRequest" />

  <output message="serviceNS:InstallHandlerResponse" />

  </operation>

  </portType>

- <binding name="PSIPCHandlerSoapBinding" type="serviceNS:PSIPCHandlerPortType">

  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />

- <operation name="Connect">

  <soap:operation soapAction="" style="rpc" />

- <input>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </input>

- <output>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </output>

  </operation>

- <operation name="Ping">

  <soap:operation soapAction="" style="rpc" />

- <input>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </input>

- <output>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </output>

  </operation>

- <operation name="ConnectNewPowerPlant">

  <soap:operation soapAction="" style="rpc" />

- <input>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </input>

- <output>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </output>

  </operation>

- <operation name="InstallHandler">

  <soap:operation soapAction="" style="rpc" />

- <input>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </input>

- <output>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </output>

  </operation>

- <operation name="InstallHandler">

  <soap:operation soapAction="" style="rpc" />

- <input>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </input>

- <output>

  <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler" use="encoded" />

  </output>

  </operation>

  </binding>

- <service name="PSIPCHandler">

- <port binding="serviceNS:PSIPCHandlerSoapBinding" name="PSIPCHandlerPort">

  <soap:address location="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet" />

  </port>

  </service>

  </definitions>

 

 

Below is a screen shot of the web service schema for the class in the above example from a browser client which displays formatted schema WSDL XSD XML:

 

A current limitation is that Web services do not support overloading of methods.  The Web services model does not support it so you must create additional PowerKernel method wrappers to disambiguate them to Web services.
Dr. David Gelernter at Yale

 

 

The Packet Class as Tuple

 

The Tuple Space or Linda model of computing was developed as a parallel computing architecture.  A great contributor to the field, Gelernter, devised a simple protocol for complex data structures composed of arbitrary fields of 'tuples' to operate within a 'space'; or shared communications buffer.  The fields in the tuples can be of formal or value (aka actual) type.  The formal type is analogous to the Java Class class, or class definition.  Formal types are used in the space to denote a template that may match others of formal type or instances of that formal type.  Actual types are instances of classes.  The operations on the space are simplicity themselves; in, out, and waitFor.  Others functions such as read without taking out are supported to different degrees in variations of the model.

 

'in' puts a tuple in the space.  'out' removes all instances of a tuple matching the supplied tuple and returns them to the caller.  'waitFor' is used to block until a tuple matching the target enters the space, at which time it is removed and sent to the caller.

 

Using these simple constructs, a world of parallel computing can be realized.  The Packet class in PowerKernel is analogous to the tuple in other systems.  The Packet constructor can be called with an arbitrary argument list of objects to be packaged in the tuple.

 

import com.neocoretechs.powerspaces.*;

import java.io.*;

import java.util.*;

public class space1

{

        /**

        * Connect to kernel running and invoke space

        * @param argv[0] The host to connect to

        */

        public static void main(String[] argv) throws Exception {

                        PowerSpace PS = new PowerSpace(argv[0]);

                        PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");

                                System.out.println(pkr.invoke("in",new Packet("this","is","a",new Integer(1),new Integer(2),new Integer(3))));

                                System.out.println(pkr.invoke("in",new Packet("this","is","a",new Integer(1),new Integer(2),new Integer(4))));

                                System.out.println(pkr.invoke("in",new Packet("this","is","a",new Integer(1),new Integer(2),new Integer(5))));

                                System.out.println(pkr.invoke("in",new Packet("this","is","a",new Integer(1),new Integer(2),new Integer(6))));

                                System.out.println(pkr.invoke("in",new Packet("this","is","a",new Integer(1),new Integer(2),new Long(7))));

                        PS.Unplug();

        }

}

 

Notice that we are placing 5 similar tuples in the space, but the last one has an argument of type Long.  We now construct a module to retrieve only the Packets matching those with the last argument of type Integer:

 

import com.neocoretechs.powerspaces.*;

import java.io.*;

import java.util.*;

public class space2

{

        /**

        * Connect to kernel running and invoke space

        * @param argv[0] The host to connect to

        */

        public static void main(String[] argv) throws Exception {

                        PowerSpace PS = new PowerSpace(argv[0]);

                        PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");

                        // construct a template to get Tuples

                        System.out.println(pkr.invoke("out",new Packet("this","is","a",new Integer(1),new Integer(2),new Field(Integer.class))));

                        PS.Unplug();

        }

}

 

The result being returned is a Packet of Packets retrieved from the space:

 

[ [ "this", "is", "a", 1, 2, 3 ], [ "this", "is", "a", 1, 2, 4 ], [ "this", "is"

, "a", 1, 2, 5 ], [ "this", "is", "a", 1, 2, 6 ] ]

 

In the format displayed by the Packet class containing other Packets.  Notice the absence of '7'.  Since it was of formal type Long the template did not match it.

 

import com.neocoretechs.powerspaces.*;

import java.io.*;

import java.util.*;

public class space3

{

        /**

        * Connect to kernel and invoke space

        * @param argv[0] The URI to connect to

        */

        public static void main(String[] argv) throws Exception {

                        PowerSpace PS = new PowerSpace(argv[0]);

                        PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");

                        // construct a template to get Tuples

                        System.out.println(pkr.invoke("waitFor",new Packet("this","is","a",new Integer(1),new Integer(2),new Field(Long.class))));

                        PS.Unplug();

        }

}

 

This method will block until the matching tuple appears.  If we run space1 again from another client at the same time, the last tuple inserted will, match and this client will return.


Cluster Architecture

 

 

The cluster is designed to be connected in a geodesic web as illustrated below.  Up to 9223 trillion nodes may be connected.

 

The nodes are connected using persistent TCP/IP socket connections.  On for inbound traffic and another for outbound on each 'leg' of the node.  The legs are designated as Left, Right, Parent, Left Crossbar and Right Crossbar.  A parent must connect a child node into the cluster from its left or right legs to the child's parent ports.  A crossbar connection is made from the right node/left crossbar legs to left node/right crossbar.   Edge nodes are joined the same way. Here, this form of quorum adds a measure of security.  It is not possible to connect into the cluster without action by the parent, however, this action can be performed programatically, so the cluster may be able to maliciously connect if compromised.  Traffic through the cluster is passed from node to node using the Packet class described above.  Transport through the cluster using the Packet class is necessary due to the need to add routing and messaging elements to the Packet dynamically.  In addition, the results of a parallel operation involving an arbitrary number of nodes requires a Packet to represent the unreduced results if that is the goal of the application.  Subtypes of Packet are used for routing and point-to-point messaging and a Packet may transported through the cluster contains a command payload which may be broadcast for parallel operations or a point-to-point message.

 

Synchronizing and reduction queues on each node provide barrier synchronization.

 

 

Cluster

 

A configuration file called PowerSpaces.ini in the directory from which the PowerPlant was started can be used to set the port ranges.  The ranges are from the main port to main port+11, since multiple sockets are used for interconnect.  The format of the file is as follows:

MainPort:8202

ParentInBind:127.0.0.1

ParentOutBind:127.0.0.1

LeftInBind:127.0.0.1

LeftOutBind:127.0.0.1

RightInBind:127.0.0.1

RightOutBind:127.0.0.1

LeftCrossbarInBind:127.0.0.1

LeftCrossbarOutBind:127.0.0.1

RightCrossbarInBind:127.0.0.1

RightCrossbarOutBind:127.0.0.1

 

Each input and output port on each leg may be bound to specific network interfaces for increased throughput.

 

The following module may be used to connect clustered PowerKernels:
import com.neocoretechs.powerspaces.*;

import java.io.*;

import java.util.*;

public class connect

{

        public static void main(String[] argv) {

                try  {

                       PowerSpace PS = new PowerSpace(argv[0], 8202);

                       PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSIPCHandler");

                       PKRemote pkp = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PKParallel");

                       long st = System.currentTimeMillis();

              

                       System.out.println("Start time "+String.valueOf(st));

                       Packet ppp = (Packet)(pkp.invoke("BroadcastAll",

                                new Packet("com.neocoretechs.powerspaces.server.handler.PSIPCHandler",

                                "FindConnectPoint", new Packet())) );

 

System.out.println(ppp);

 

                     Packet pp = (Packet)(ppp.getField(0).value());

                     Long tclusterID = (Long)(pp.getField(0).value());

                        ppp = (Packet)(pkp.invoke("SendMessage",

                                new Packet(tclusterID, "com.neocoretechs.powerspaces.server.handler.PSIPCHandler","ConnectNewPowerPlant",

                                new Packet(new Integer(8302),argv[1], argv[2]))) );

                      

System.out.println("Cluster synch time "+String.valueOf(System.currentTimeMillis()-st)+" ms");

       System.out.println(ppp);

                } catch(Exception e) { System.out.println(e); e.printStackTrace(); }

        }

}

 

The arguments to the above module are the PowerKernel root node, the host name of the node that is being connected to for the input and the host for the output (typically the same, though multiple network interfaces can be used on one machine for higher throughput).  So for two PowerKernels running on the same node with base ports 8202 and 8203 as above, the command line would be: java connect localhost localhost localhost. The first invocation broadcasts the method call FindConnectPoint using the BroadcastAll method of com.neocoretechs.powerspaces.server.handler.PKParallel.  BroadcastAll takes a Packet of class, method, and return Packet.  The return packet can be manipulated by each node and returned to the caller after its journey through the cluster.  FindConnectPoint begins execution on each node in the cluster.  Each node determines the number of unused legs it possesses, and returns that number and its id to the parent node.  The parent node determines the most eligible leg and returns that to its parent.  The reduction is performed at each node until the root, where the most eligible has been reduced by the nodes below.  This target is sent a message to connect the new node in the second invocation: ConnectNewPowerPlant, whose first argument is the target cluster Id of the most eligible cluster node reduction. The SendMessage method takes a cluster Id, a class name and a method, and a Packet payload containing the arguments for that method.

 

Other methods of the PKParallel class permit balanced processing.  The Balance method takes a command payload packet and distributes it to the next available node for execution based on a spinner located at each node.  A Collect method is used to gather results from the reduction queues.  The methods themselves, by using the CustomerConnectionPanel and ConnectionPanel, and the Leg argument obtain the necessary interaction to perform a wide range of parallel processing.  By mastering the above concepts and applying the same methods using the connection example, application scalability can be achieved in a manner beyond the usual high-availability and load balancing schemes.